package com.qubuyer.core.http

import com.google.gson.JsonParser
import com.qubuyer.core.ext.toJson
import okhttp3.Interceptor
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okio.Buffer
import java.io.UnsupportedEncodingException
import java.nio.charset.Charset
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*

/**
 * Author: linwg
 * Time: 2021/2/4 0004
 * Description:
 */
class SignInterceptor : Interceptor {

    private val salt = "qumaimai20201215"

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val method = request.method
        var charset = Charset.forName("UTF-8")
        if (method.toLowerCase(Locale.getDefault()) == "post") {
            val body = request.body
            val contentType = body?.contentType()
            charset = contentType?.charset(charset)
            if (contentType?.type?.toLowerCase(Locale.getDefault()) == "multipart") {
                return chain.proceed(request)
            } else {
                val buffer = Buffer()
                body?.writeTo(buffer)
                val bodyString = if(body == null)  "" else buffer.readString(charset)
                when (body?.contentType()?.subtype) {
                    "json" -> {
                        val je = JsonParser.parseString(bodyString)
                        val keySet = je.asJsonObject.keySet()
                        val treeMap = TreeMap<String, String>()
                        keySet.forEach { key ->
                            treeMap[key] = je.asJsonObject[key].asString
                        }
                        treeMap["sign"] = getSign(treeMap)
                        val newRequestBody = treeMap.toJson().toRequestBody(body.contentType())
                        val newRequest = request.newBuilder().post(newRequestBody).build()
                        return chain.proceed(newRequest)
                    }
                    "x-www-form-urlencoded" -> {
                        val split = bodyString.split("&")
                        val treeMap = TreeMap<String, String>()
                        split.forEach { params ->
                            val kv = params.split("=")
                            treeMap[kv[0]] = kv[1]
                        }
                        treeMap["sign"] = getSign(treeMap)
                        val append = if (bodyString.isNotEmpty()) "&" else ""
                        val newRequestBody = "$bodyString${append}timestamp=${treeMap["timestamp"]}&sign=${treeMap["sign"]}".toRequestBody(body.contentType())
                        val newRequest = request.newBuilder().post(newRequestBody).build()
                        return chain.proceed(newRequest)
                    }
                    else -> {
                        return chain.proceed(request)
                    }
                }
            }
        } else if (method.toLowerCase(Locale.getDefault()) == "get") {
            val queryParameterNames = request.url.queryParameterNames
            val treeMap = TreeMap<String, String>()
            queryParameterNames.forEachIndexed { index, name ->
                treeMap[name] = request.url.queryParameterValue(index) ?: ""
            }
            treeMap["sign"] = getSign(treeMap)
            val newUrl = request.url.newBuilder().addQueryParameter("timestamp", treeMap["timestamp"]).addQueryParameter("sign", treeMap["sign"]).build()
            val newRequest = request.newBuilder().get().url(newUrl).build()
            return chain.proceed(newRequest)
        }
        return chain.proceed(request)
    }

    private fun getSign(map: TreeMap<String, String>): String {
        map["timestamp"] = System.currentTimeMillis().toString().substring(0, 10)
        val entries: Set<Map.Entry<String, String>> = map.entries
        val iterator = entries.iterator()
        val stringBuilder = StringBuilder()
        while (iterator.hasNext()) {
            val entry = iterator.next()
            stringBuilder.append(entry.key)
            stringBuilder.append("=")
            stringBuilder.append(entry.value)
            stringBuilder.append("&")
        }
        var s = stringBuilder.toString()
        if (s.isNotEmpty()) {
            s = s.substring(0, s.length - 1)
        }
        s += salt
        return md5(s).toUpperCase(Locale.getDefault())
    }

    private fun md5(`in`: String): String {
        val hash: ByteArray
        hash = try {
            MessageDigest.getInstance("MD5").digest(`in`.toByteArray(charset("UTF-8")))
        } catch (e: NoSuchAlgorithmException) {
            throw RuntimeException("Huh, MD5 should be supported?", e)
        } catch (e: UnsupportedEncodingException) {
            throw RuntimeException("Huh, UTF-8 should be supported?", e)
        }
        val hex = java.lang.StringBuilder(hash.size * 2)
        for (b in hash) {
            if (b.toInt() and 0xFF < 0x10) hex.append("0")
            hex.append(Integer.toHexString(b.toInt() and 0xFF))
        }
        return hex.toString()
    }
}